/**
 * \file vector3.hpp
 * 
 * \section COPYRIGHT
 *
 * Basis: A 3D Mathematics Library
 *
 * ---------------------------------------------------------------------
 *
 * Copyright (c) 2010, Don Olmstead
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *
 *  3. Neither the name of organization nor the names of its contributors may be
 *     used to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef BASIS_VECTOR3_HPP_INCLUDED
#define BASIS_VECTOR3_HPP_INCLUDED

#include <ostream>
#include <basis/constants.hpp>
#include <basis/simd/types.hpp>

namespace basis
{
	template <typename Real, typename InstructionSet = simd::none>
	class vector3
	{

		//------------------------------------------------------------
		// Member types
		//------------------------------------------------------------

		public:

			/// Value of a real number
			typedef Real value_type;
			/// Reference to a real number
			typedef Real& reference;

		//------------------------------------------------------------
		// Construction/Destruction
		//------------------------------------------------------------

		public:

			/**
			 * Creates an instance of the vector3 class.
			 *
			 * Uninitialized to speed creation.
			 */
			vector3()
			{ }

			/**
			 * Destroys an instance of the vector3 class.
			 */
			~vector3()
			{ }

			/**
			 * Initializes an instance of the vector3 class.
			 *
			 * \param x The x component of the vector.
			 * \param y The y component of the vector.
			 * \param z The z component of the vector.
			 */
			vector3(Real x, Real y, Real z)
			{
				_values[0] = x;
				_values[1] = y;
				_values[2] = z;
			}

		//------------------------------------------------------------
		// Comparison operators
		//------------------------------------------------------------

		public:

			/**
			 * Determines whether two vectors are equal.
			 *
			 * \param compare The vector to compare with.
			 * \returns \b true if the instances are equal; \b false otherwise.
			 */
			inline bool operator== (const vector3& compare) const
			{
				return memcmp(_values, compare._values, 3 * sizeof(Real)) == 0;
			}

			/**
			 * Determines whether two vectors are not equal.
			 *
			 * \param compare The vector to compare with.
			 * \returns \b true if the instances are not equal; \b false otherwise.
			 */
			inline bool operator!= (const vector3& compare) const
			{
				return memcmp(_values, compare._values, 3 * sizeof(Real)) != 0;
			}

		//------------------------------------------------------------
		// Vector operations
		//------------------------------------------------------------

		public:

			inline value_type x() const
			{
				return _values[0];
			}

			inline reference x()
			{
				return _values[0];
			}

			inline value_type y() const
			{
				return _values[1];
			}

			inline reference y()
			{
				return _values[1];
			}

			inline value_type z() const
			{
				return _values[2];
			}

			inline reference z()
			{
				return _values[2];
			}

			inline value_type length() const
			{
				return math<Real>::sqrt(
					_values[0] * _values[0] +
					_values[1] * _values[1] +
					_values[2] * _values[2]);
			}

			inline value_type length_squared() const
			{
				return
					_values[0] * _values[0] +
					_values[1] * _values[1] +
					_values[2] * _values[2];
			}

		//------------------------------------------------------------
		// Vector operations
		//------------------------------------------------------------

		public:

			inline vector3 operator+ (const vector3& rhs) const
			{
				return vector3(
					_values[0] + rhs._values[0],
					_values[1] + rhs._values[1],
					_values[2] + rhs._values[2]);
			}

			inline vector3 operator- (const vector3& rhs) const
			{
				return vector3(
					_values[0] - rhs._values[0],
					_values[1] - rhs._values[1],
					_values[2] - rhs._values[2]);
			}

			inline vector3 operator* (Real rhs) const
			{
				return vector3(
					_values[0] * rhs,
					_values[1] * rhs,
					_values[2] * rhs);
			}

		//------------------------------------------------------------
		// Vector operations
		//------------------------------------------------------------

		public:

			inline void normalize()
			{
				Real inverse_length = length();

				_values[0] *= inverse_length;
				_values[1] *= inverse_length;
				_values[2] *= inverse_length;
			}

			template <typename Real, typename InstructionSet>
			friend inline vector3<Real, InstructionSet> normalize(const vector3<Real, InstructionSet>& vector)
			{
				Real inverse_length = vector.length();

				return vector3<Real, InstructionSet>(
					vector._values[0] * inverse_length,
					vector._values[1] * inverse_length,
					vector._values[2] * inverse_length);
			}

			template <typename Real, typename InstructionSet>
			friend inline Real dot(const vector3<Real, InstructionSet>& lhs, const vector3<Real, InstructionSet>& rhs)
			{
				return
					lhs._values[0] * rhs._values[0] +
					lhs._values[1] * rhs._values[1] +
					lhs._values[2] * rhs._values[2];
			}

			template <typename Real, typename InstructionSet>
			friend inline vector3<Real, InstructionSet> cross(const vector3<Real, InstructionSet>& lhs, const vector3<Real, InstructionSet>& rhs)
			{
				return vector3<Real, InstructionSet>(
					(lhs._values[1] * rhs._values[2]) - (lhs._values[2] * rhs._values[1]),
					(lhs._values[2] * rhs._values[0]) - (lhs._values[0] * rhs._values[2]),
					(lhs._values[0] * rhs._values[1]) - (lhs._values[1] * rhs._values[0]));
			}

		//------------------------------------------------------------
		// Stream output
		//------------------------------------------------------------

			template <typename Real, typename InstructionSet>
			friend inline std::ostream& operator<< (std::ostream& output, const vector3<Real, InstructionSet>& vector)
			{
				output 
					<< "{ X: " << vector._values[0]
					<< ", Y: " << vector._values[1]
					<< ", Z: " << vector._values[2] << "}";

				return output;
			}

		//------------------------------------------------------------
		// Common vectors
		//------------------------------------------------------------

		public:

			static inline vector3 zero()
			{
				return vector3((Real)0, (Real)0, (Real)0);
			}

			static inline vector3 one()
			{
				return vector3((Real)1, (Real)1, (Real)1);
			}

			static inline vector3 unit_x()
			{
				return vector3((Real)1, (Real)0, (Real)0);
			}

			static inline vector3 unit_y()
			{
				return vector3((Real)0, (Real)1, (Real)0);
			}

			static inline vector3 unit_z()
			{
				return vector3((Real)0, (Real)0, (Real)1);
			}

		private:

			value_type _values[3];

	} ; // end class vector3

} // end namespace basis

#endif // end BASIS_VECTOR3_HPP_INCLUDED
